package hu.mapro.gwtui.gxt.client.data;

import hu.mapro.gwtui.client.browser.LoadResult;
import hu.mapro.gwtui.client.browser.Loader;
import hu.mapro.gwtui.client.data.NamedSortField;
import hu.mapro.gwtui.client.data.SortField;
import hu.mapro.gwtui.client.rpc.ConverterAsyncCallback;
import hu.mapro.model.Getter;
import hu.mapro.model.Setter;
import hu.mapro.model.meta.Field;

import java.util.List;

import com.google.common.collect.Lists;
import com.google.gwt.core.client.Callback;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.ServerFailure;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.SortInfo;
import com.sencha.gxt.data.shared.loader.ListLoadConfig;
import com.sencha.gxt.data.shared.loader.ListLoadResult;
import com.sencha.gxt.data.shared.loader.ListLoadResultBean;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoadResultBean;
import com.sencha.gxt.data.shared.loader.RequestFactoryProxy;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.HasSelectionChangedHandlers;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
 
public class GxtDataUtils {

	public static List<SortField> createSortFields(
			List<? extends SortInfo> sortInfo
	) {
		List<SortField> fields = Lists.newArrayList();

		for (SortInfo si : sortInfo) {
			NamedSortField sf = new NamedSortField(); 
			sf.setAscending(si.getSortDir()==SortDir.ASC);
			sf.setName(si.getSortField());
			
			fields.add(sf);
		}
		
		return fields;
	}
	
	public static <T> Getter<T, String> getFunction(final ModelKeyProvider<T> vp) {
		return new Getter<T, String>() {
			@Override
			public String get(T input) {
				return vp.getKey(input);
			}
		};
	}

	public static <T, F extends Field&Getter<? super T, V>, V> ValueProvider<T, V> toValueProvider(final F field) {
		return new ValueProvider<T, V>() {

			@Override
			public V getValue(T object) {
				return field.get(object);
			}

			@Override
			public void setValue(T object, V value) {
				throw new UnsupportedOperationException("setValue not implemented for columns");
			}

			@Override
			public String getPath() {
				return field.getName();
			}
		};
	}
	
	public static <T, F extends Field&Getter<T, V>&Setter<T, V>, V> ValueProvider<T, V> toWritableValueProvider(final F field) {
		return new ValueProvider<T, V>() {
			
			@Override
			public V getValue(T object) {
				return field.get(object);
			}
			
			@Override
			public void setValue(T object, V value) {
				field.set(object, value);
			}
			
			@Override
			public String getPath() {
				return field.getName();
			}
		};
	}

	public static <T> RequestFactoryProxy<ListLoadConfig, ListLoadResult<T>> createProxy(
			final Loader<T> loader) {
		return new RequestFactoryProxy<ListLoadConfig, ListLoadResult<T>>() {
	        @Override
	        public void load(final ListLoadConfig loadConfig, final Receiver<? super ListLoadResult<T>> receiver) {
	        	loader.load(-1, -1, createSortFields(loadConfig.getSortInfo()), new Callback<LoadResult<T>, Throwable>() {
	
					public void onFailure(Throwable reason) {
						receiver.onFailure(new ServerFailure(reason.getMessage()));
					}
	
					@Override
					public void onSuccess(LoadResult<T> result) {
						receiver.onSuccess(new ListLoadResultBean<T>(result.getRecords()));
					}
				});
	        }
	    };
	}
	
	public static <T> RequestFactoryProxy<PagingLoadConfig, PagingLoadResult<T>> createPagingProxy(
			final Loader<T> loader) {
		return new RequestFactoryProxy<PagingLoadConfig, PagingLoadResult<T>>() {
			@Override
			public void load(final PagingLoadConfig loadConfig, final Receiver<? super PagingLoadResult<T>> receiver) {
				loader.load(loadConfig.getOffset(), loadConfig.getLimit(), createSortFields(loadConfig.getSortInfo()), new Callback<LoadResult<T>, Throwable>() {
					
					public void onFailure(Throwable reason) {
						receiver.onFailure(new ServerFailure(reason.getMessage()));
					}
					
					@Override
					public void onSuccess(LoadResult<T> result) {
						receiver.onSuccess(new PagingLoadResultBean<T>(result.getRecords(), (int) result.getCount(), loadConfig.getOffset()));
					}
				});
			}
		};
	}

	public static <T> com.google.web.bindery.event.shared.HandlerRegistration whenHasSelection(
			HasSelectionChangedHandlers<T> source,
			final HasEnabled hasEnabled
	) {
		return source.addSelectionChangedHandler(new SelectionChangedHandler<T>() {
			@Override
			public void onSelectionChanged(SelectionChangedEvent<T> event) {
				hasEnabled.setEnabled(!event.getSelection().isEmpty());
			}
		});
	}

	public static <T> AsyncCallback<List<T>> createListCallback(
			AsyncCallback<ListLoadResult<T>> callback
	) {
		return new ConverterAsyncCallback<List<T>, ListLoadResult<T>>(callback) {
			@Override
			protected ListLoadResult<T> convertResult(List<T> result) {
				return new ListLoadResultBean<T>(result);
			}
		};
	}
	
	public static final ModelKeyProvider<Enum<?>> ENUM_MODEL_KEY_POVIDER = new ModelKeyProvider<Enum<?>>() {
		@Override
		public String getKey(Enum<?> item) {
			return item.name();
		}
	};
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static final <T extends Enum<T>> ModelKeyProvider<T> enumModelKeyProvider() {
		return (ModelKeyProvider) ENUM_MODEL_KEY_POVIDER;
	}
	
	public static <T> LabelProvider<T> labelProvider(final Getter<T, String> getter) {
		return new LabelProvider<T>() {
			@Override
			public String getLabel(T item) {
				return getter.get(item);
			}
		};
	}
	
}
